package com.mrogrape.notepad.ui.library;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.mrogrape.util.StringUtils;

public class LibrariesManager {

	File file;

	public LibrariesManager(File dir, String fileName) {

		this(new File(dir, fileName));
	}

	public LibrariesManager(File file) {

		if (file == null) {
			throw new NullPointerException("file cannot be null.");
		}

		this.file = file;
	}

	public List<Library> load() throws XMLStreamException, FactoryConfigurationError, IOException {

		List<Library> libraries = new LinkedList<Library>();

		InputStream is = null;
		try {
			is = new FileInputStream(this.file);
			XMLStreamReader parser = XMLInputFactory.newInstance().createXMLStreamReader(is);

			Library currentLibrary = null;
			List<Inclusion> inclus = new LinkedList<Inclusion>();
			while (parser.hasNext()) {

				int event = parser.next();
				// Element Start
				if (event == XMLStreamConstants.START_ELEMENT) {

					String elementName = parser.getLocalName();

					// Library Start
					if (LibrariesManager.NODE_LIBRARY.equals(elementName)) {

						currentLibrary = new Library(parser.getAttributeValue(null, LibrariesManager.ATTRIBUTE_LIBRARY_NAME));
					}
					// Inclusion Start
					if (LibrariesManager.NODE_INCLUSION.equals(elementName)) {

						String name = parser.getAttributeValue(null, LibrariesManager.ATTRIBUTE_INCLUSION_NAME);
						String type = parser.getAttributeValue(null, LibrariesManager.ATTRIBUTE_INCLUSION_TYPE);
						String href = parser.getAttributeValue(null, LibrariesManager.ATTRIBUTE_INCLUSION_HREF);
						String visible = parser.getAttributeValue(null, LibrariesManager.ATTRIBUTE_INCLUSION_VISIBLE);
						String expanded = parser.getAttributeValue(null, LibrariesManager.ATTRIBUTE_INCLUSION_EXPANDED);

						Inclusion incl = new Inclusion(name, type, href);
						if (StringUtils.isNotEmpty(visible)) {
							incl.setVisible(Boolean.parseBoolean(visible));
						}
						if (StringUtils.isNotEmpty(expanded)) {
							incl.setExpanded(Boolean.parseBoolean(expanded));
						}
						inclus.add(incl);
					}
				}
				else if (event == XMLStreamConstants.END_ELEMENT) {

					String elementName = parser.getLocalName();

					// Library End
					if (LibrariesManager.NODE_LIBRARY.equals(elementName)) {
						for (Inclusion inclu : inclus) {
							currentLibrary.includeInclusion(inclu);
						}
						inclus.clear();

						libraries.add(currentLibrary);
					}
				}
			}

		}
		catch (FileNotFoundException e) {
			e.printStackTrace();
			return new LinkedList<Library>();
		}
		finally {
			is.close();
		}

		return libraries;
	}

	public void save(List<Library> libraries) throws ParserConfigurationException, FileNotFoundException, TransformerException {

		DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		Document doc = builder.newDocument();
		Element rootNode = doc.createElement(LibrariesManager.NODE_LIBRARIES);

		// 遍历库，构建库节点
		for (Library lib : libraries) {

			Element libNode = doc.createElement(LibrariesManager.NODE_LIBRARY);
			libNode.setAttribute(LibrariesManager.ATTRIBUTE_LIBRARY_NAME, lib.getName());

			// 遍历导入对象，构建导入对象节点
			for (Inclusion inclusion : lib.getIncludes()) {

				Element incluNode = doc.createElement(LibrariesManager.NODE_INCLUSION);
				// Name
				incluNode.setAttribute(LibrariesManager.ATTRIBUTE_INCLUSION_NAME, inclusion.getName());
				// Type
				incluNode.setAttribute(LibrariesManager.ATTRIBUTE_INCLUSION_TYPE, inclusion.getType());
				// Href
				incluNode.setAttribute(LibrariesManager.ATTRIBUTE_INCLUSION_HREF, inclusion.getHref());
				// Expanded
				incluNode.setAttribute(LibrariesManager.ATTRIBUTE_INCLUSION_EXPANDED, String.valueOf(inclusion.isExpanded()));
				// Visible
				incluNode.setAttribute(LibrariesManager.ATTRIBUTE_INCLUSION_VISIBLE, String.valueOf(inclusion.isVisible()));

				// 将包含项目节点添加至库节点
				libNode.appendChild(incluNode);
			}

			// 将库节点添加至根节点
			rootNode.appendChild(libNode);
		}

		// 将根元素添加至文档
		doc.appendChild(rootNode);

		// construct the "do nothing" transformation
		Transformer t = TransformerFactory.newInstance().newTransformer();
		// set indentation
		t.setOutputProperty(OutputKeys.INDENT, "yes");
		t.setOutputProperty(OutputKeys.METHOD, "xml");
		t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
		// apply the "do nothing" transformation and send the output to a file
		t.transform(new DOMSource(doc), new StreamResult(new FileOutputStream(this.file)));

	}

	private static final String NODE_LIBRARIES = "libraries";
	private static final String NODE_LIBRARY = "library";
	private static final String NODE_INCLUSION = "inclusion";

	private static final String ATTRIBUTE_LIBRARY_NAME = "name";

	private static final String ATTRIBUTE_INCLUSION_NAME = "name";
	private static final String ATTRIBUTE_INCLUSION_TYPE = "type";
	private static final String ATTRIBUTE_INCLUSION_HREF = "href";
	private static final String ATTRIBUTE_INCLUSION_VISIBLE = "visible";
	private static final String ATTRIBUTE_INCLUSION_EXPANDED = "expanded";

}
